package com.example.annotation;

import org.springframework.amqp.core.Queue;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.*;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class DeclareQueueAnnotationBeanPostProcessor implements BeanFactoryAware, BeanPostProcessor {

    private BeanFactory beanFactory;

    private BeanExpressionResolver resolver=new StandardBeanExpressionResolver();

    private BeanExpressionContext expressionContext;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory=beanFactory;
        if (beanFactory instanceof ConfigurableListableBeanFactory){
            this.resolver=((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
            this.expressionContext=new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
        }
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        List<DeclareQueue> collect = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
                .stream(DeclareQueue.class)
                .map(ann -> ann.synthesize())
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(collect)){
            Iterator<DeclareQueue> iterator = collect.iterator();

            while (iterator.hasNext()){
                DeclareQueue next = iterator.next();
                String name=(String) this.resolverExpression(next.prefixQueueName());
                Integer size=Integer.parseInt((String) this.resolverExpression(next.QueueSize()));
                for (int i = 0; i < size; i++) {
                    String s = name + "_" + (i + 1);
                    this.declareQueue(s);
                }
            }
        }
        return bean;
    }

    private String declareQueue(String queueName){
        String finalQueueName=(String)this.resolverExpression(queueName);
        if (StringUtils.isEmpty(finalQueueName)){
            return queueName;
        }else {
            Map args=new HashMap();
            args.put("x-queue-type","classic");
            Queue queue = new Queue(finalQueueName, true, false, false, args);
            queue.setIgnoreDeclarationExceptions(true);
            if (!this.beanFactory.containsBean(queueName)){
                ((ConfigurableBeanFactory)this.beanFactory).registerSingleton(queueName, queue);
            }
            queue.setShouldDeclare(true);
            return queueName;
        }
    }

    private Object resolverExpression(String value){
        String resolverValue = this.resolver(value);
        return this.resolver.evaluate(resolverValue,this.expressionContext);
    }

    private String resolver(String value) {
        return this.beanFactory!=null && this.beanFactory instanceof ConfigurableBeanFactory ?
                ((ConfigurableBeanFactory)beanFactory).resolveEmbeddedValue(value):value;
    }
}
